package Model;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

public class Computation {
	
	String polynomial;
	double startInterval;
	double endInterval;
	double tolerance;
	int bracketingIteration;
	double startPoint;
	int openIteration;
	double newtonThreshold;
	
	private LinkedHashMap<Double, Double> equation;
	private LinkedHashMap<Double, Double> integratedEquation;
	
	private ArrayList<Double> firstColumn;
	private ArrayList<Double> secondColumn;
	private ArrayList<Double> thirdColumn;
	
	private ArrayList<Double> columnOne;
	private ArrayList<Double> columnTwo;
	private ArrayList<Double> columnThree;
	private ArrayList<Double> columnFour;
	private ArrayList<Double> columnFive;
	private ArrayList<Double> columnSix;;
	private ArrayList<Double> columnSeven;
	
	private ArrayList<Double> x;
	private ArrayList<Double> y;
	
	
	public Computation(String polynomial, double startInterval, double endInterval, double tolerance, int bracketingIteration, double startPoint, int openIteration, double newtonThreshold){
		this.polynomial = polynomial;
		this.startInterval = startInterval;
		this.endInterval = endInterval;
		this.tolerance = tolerance;
		this.bracketingIteration = bracketingIteration;
		this.startPoint = (double) startPoint;
		this.openIteration = openIteration;
		this.newtonThreshold = newtonThreshold;
		
		equation = new LinkedHashMap<Double, Double>();
		
		String[] temp = polynomial.split(" ");

		for(int i = 0; i < temp.length; i ++){
			equation.put(Double.parseDouble(temp[i]), Double.parseDouble(temp[i+1]));
			i++;
		}
		computePolynomial(equation, (double) startInterval, (double) endInterval);
		computeNewton(equation, (double) startPoint, openIteration);		
		computeBisection(equation, (double) startInterval, (double) endInterval, (double)  tolerance, bracketingIteration);
	}

	public void computePolynomial(LinkedHashMap<Double,Double> equation, Double startInterval, Double endInterval){
		Double iterations = startInterval - 10;
		x = new ArrayList<>();
		y = new ArrayList<>();
			
		while(endInterval + 10 >= iterations){
			Double temp = 0.0;
			x.add(iterations);
			for(Entry<Double, Double> e : equation.entrySet()){
				temp += e.getKey() * Math.pow(iterations, e.getValue());
			}
			y.add(temp);
			iterations ++;
		}
	}
	
	public void computeNewton(LinkedHashMap<Double,Double> equation, Double startPoint, int openIteration){
		integratedEquation = new LinkedHashMap<Double, Double>();
		firstColumn = new ArrayList<Double>();
		secondColumn = new ArrayList<Double>();
		thirdColumn = new ArrayList<Double>();
		
		double difference = 0.0;
	
		for(Entry<Double, Double> e : equation.entrySet()){
			Double first = e.getKey() * e.getValue();
			Double second = e.getValue() - 1;
				if(first != 0.0)
					integratedEquation.put(first, second);
		}		
		
		while (openIteration > 0){ 
			
			firstColumn.add(startPoint);
			Double fx = 0.0;
			
			for(Entry<Double, Double> e : equation.entrySet()){
					fx += e.getKey() * Math.pow(startPoint, e.getValue());
			}

			secondColumn.add(fx);
			Double fxPrime = 0.0;
			
			for(Entry<Double, Double> e : integratedEquation.entrySet()){
				fxPrime += e.getKey() * Math.pow(startPoint, e.getValue());
			}
			
			thirdColumn.add(fxPrime);
			Double newX = 0.0;
			
			newX = startPoint - fx / fxPrime;
			difference = newX - startPoint;

			if(difference < 0)
				difference *= -1;
			
			startPoint = newX;
			
			if(difference <= newtonThreshold)
				break;
			
			openIteration --;
		}
	}
	
	public void computeBisection(LinkedHashMap<Double,Double> equation, Double startInterval, Double endInterval, Double tolerance, int iteration)
	{
		columnOne = new ArrayList<Double>();
		columnTwo = new ArrayList<Double>();
		columnThree = new ArrayList<Double>();
		columnFour = new ArrayList<Double>();
		columnFive = new ArrayList<Double> ();
		columnSix = new ArrayList<Double>();
		columnSeven = new ArrayList<Double>();
		
		Double y0 = 0.0;
		Double y1 = 0.0;
		Double diff = 0.0;
		Double x2 = 0.0;
		Double y2 = 0.0;
		
			
		for(Entry<Double, Double> e : equation.entrySet()){
			y0 += e.getKey() * Math.pow(startInterval, e.getValue());
		}
		for(Entry<Double, Double> e : equation.entrySet()){	
			y1 += e.getKey() * Math.pow(endInterval, e.getValue());
		}
		
		while(iteration > 0) 
		{
			x2 = (startInterval+endInterval)/2;
		
			y2 = 0.0;
			for(Entry<Double, Double> e : equation.entrySet()){
				y2 += e.getKey() * Math.pow(x2, e.getValue());
			}
			
			columnOne.add(startInterval);
			columnTwo.add(endInterval);	
			columnThree.add(y0);			
			columnFour.add(y1);	
				
			if(y2!=0)
			{
				if((y1*y2)<0)
				{
					startInterval = x2;
					y0 = y2;
				}
				else
				{
					endInterval = x2;
					y1 = y2;
				}
			}		
			
			diff = endInterval - startInterval;
			
			columnFive.add(x2);
			columnSix.add(y2);
			columnSeven.add(diff);
			
			if(y2 == 0)
				break;
			
			if(diff <= tolerance)
				break;
		
			iteration-- ;
		}
	}
	
	public ArrayList<Double> getFirstColumn() {
		return this.firstColumn;
	}

	public ArrayList<Double> getSecondColumn() {
		return this.secondColumn;
	}

	public ArrayList<Double> getThirdColumn() {
		return this.thirdColumn;
	}
	
	public ArrayList<Double> getColumnOne() {
		return this.columnOne;
	}

	public ArrayList<Double> getColumnTwo() {
		return this.columnTwo;
	}

	public ArrayList<Double> getColumnThree() {
		return this.columnThree;
	}
	
	public ArrayList<Double> getColumnFour() {
		return this.columnFour;
	}

	public ArrayList<Double> getColumnFive() {
		return this.columnFive;
	}

	public ArrayList<Double> getColumnSix() {
		return this.columnSix;
	}
	
	public ArrayList<Double> getColumnSeven() {
		return this.columnSeven;
	}

	public ArrayList<Double> getX() {
		return x;
	}

	public ArrayList<Double> getY() {
		return y;
	}
	
	
	
}
